package com.flask.colorpicker;

import com.flask.colorpicker.builder.ColorWheelRendererBuilder;
import com.flask.colorpicker.renderer.AbsColorWheelRenderer;
import com.flask.colorpicker.renderer.ColorWheelRenderer;
import com.flask.colorpicker.slider.AlphaSlider;
import com.flask.colorpicker.slider.LightnessSlider;
import com.flask.colorpicker.view.NumCalcUtil;
import ohos.agp.components.AttrSet;
import ohos.agp.components.Component;
import ohos.agp.render.Canvas;
import ohos.agp.render.Paint;
import ohos.agp.utils.Color;
import ohos.agp.window.service.Display;
import ohos.agp.window.service.DisplayAttributes;
import ohos.agp.window.service.DisplayManager;
import ohos.app.Context;
import ohos.hiviewdfx.HiLog;
import ohos.hiviewdfx.HiLogLabel;
import ohos.multimodalinput.event.TouchEvent;

import java.util.ArrayList;
import java.util.Optional;

import static com.flask.colorpicker.ColorConverUtils.hsb2rgb;
import static com.flask.colorpicker.renderer.AbsColorWheelRenderer.COLORCIRCLELIST;

/**
 * 自定义颜色转盘
 *
 * @since 2021-04-19
 */
public class ColorPickerView extends Component implements Component.DrawTask, Component.TouchEventListener {
    private static final HiLogLabel LABEL_LOG = new HiLogLabel(3, 0xD000f00, "dqh");

    /**
     * 类型
     */
    private final int showType;
    private int selectedOldColor = 0;
    private final Paint mPaint = new Paint();
    private final Paint mPaintSimple = new Paint();
    private final float[] hsv = new float[Constant.THIRD];
    private ColorWheelRenderer renderer;
    private int widthPm = 0;
    private ColorCircle currentColorCircle;
    private final ArrayList<OnColorSelectedListener> listeners = new ArrayList<>();
    private Canvas canvasSimple;
    private boolean isClickColor = false;
    private float clickX = 0;
    private float clickY = 0;
    private float updateAlpha = 1.0f;
    private float updateLightness = 1;

    // 圓環參數
    private int currentCount = 0;
    private final int density;

    /**
     * 构造函数
     *
     * @param context context
     * @param attrs attrs
     */
    public ColorPickerView(Context context, AttrSet attrs) {
        super(context, attrs);
        showType = attrs.getAttr("type").get().getIntegerValue();
        density = attrs.getAttr("density").get().getIntegerValue();
        init();
    }

    /**
     * 初始化
     */
    public void init() {
        setTouchEventListener(this);
        Optional<Display>
                display = DisplayManager.getInstance().getDefaultDisplay(this.getContext());
        DisplayAttributes displayAttributes = display.get().getAttributes();
        widthPm = displayAttributes.width;

        if (showType == 0) {
            renderer = ColorWheelRendererBuilder.getRenderer(WheelType.Flower);
        } else if (showType == 1) {
            renderer = ColorWheelRendererBuilder.getRenderer(WheelType.Flower);
        } else {
            renderer = ColorWheelRendererBuilder.getRenderer(WheelType.Circle);
        }
        setRenderer(renderer);
        AlphaSlider alphaSlider = new AlphaSlider(getContext());
        LightnessSlider lightnessSlider = new LightnessSlider(getContext());
        HiLog.info(LABEL_LOG,alphaSlider + "");
        HiLog.info(LABEL_LOG,lightnessSlider + "");

        COLORCIRCLELIST.clear();
        mPaint.reset();
        mPaintSimple.reset();
        invalidate();
        addDrawTask(this);
    }

    /**
     * 设置刷新
     *
     * @param rendererType rendererType
     */
    public void setRenderer(ColorWheelRenderer rendererType) {
        this.renderer = rendererType;
        invalidate();
    }

    /**
     * onDraw绘制
     *
     * @param component component
     * @param canvas canvas
     */
    @Override
    public void onDraw(Component component, Canvas canvas) {
        this.canvasSimple = canvas;
        final int setSize = COLORCIRCLELIST.size();
        float sizeSet = getNumber();
        float half = widthPm / sizeSet;
        float strokeWidth = Constant.STROKE_RATIO * (1f + ColorWheelRenderer.GAP_PERCENTAGE);
        float maxRadius = NumCalcUtil.subtract(NumCalcUtil.subtract(half , strokeWidth) , half / density);
        float maxSize = maxRadius / (density - 1) / Constant.SECOND;

        for (int ii = 0; ii < density; ii++) {
            float pp = (float) ii / (density - 1); // 0~1
            float jitter = (NumCalcUtil.subtract(ii , density / Constant.SECONDF)) / density; // -0.5 ~ 0.5
            float radius = maxRadius * pp;
            float size = Math.max(NumCalcUtil.add(Constant.STROKE_RATIO , strokeWidth)
                    , NumCalcUtil.add(maxSize , (ii == 0 ? 0 : maxSize * Constant.ONESECONDF * jitter)));
            int total = Math.min(AbsColorWheelRenderer.calcTotalCount(radius, size), density * Constant.SECOND);

            for (int jj = 0; jj < total; jj++) {
                double angle = Math.PI * Constant.SECOND * jj / total + (Math.PI / total)
                        * ((ii + 1) % Constant.SECOND);
                hsv[0] = (float) (angle * Constant.ONEEAT / Math.PI);
                hsv[1] = radius / maxRadius;
                hsv[Constant.SECOND] = updateLightness;
                float[] rgb = hsb2rgb(hsv);
                float xx = NumCalcUtil.add(half , (float) (radius * Math.cos(angle)));
                float yy = NumCalcUtil.add(half , (float) (radius * Math.sin(angle))); // 颜色，色调，亮度
                int colorInit = Color.rgb((int) rgb[0], (int) rgb[1], (int) rgb[Constant.SECOND]);
                mPaint.setColor(new Color(colorInit));
                mPaint.setAlpha(updateAlpha);
                canvas.drawCircle(xx, yy, NumCalcUtil.subtract(size , strokeWidth), mPaint);

                if (currentCount >= setSize) {
                    COLORCIRCLELIST.add(new ColorCircle(xx, yy, hsv));
                } else {
                    COLORCIRCLELIST.get(currentCount).set(xx, yy, hsv);
                }
                currentCount++;
            }
        }
        if (isClickColor) {
            int updatecolor = ColorConverUtils.colorAtLightness(selectedOldColor, updateLightness);
            mPaintSimple.setColor(new Color(updatecolor)); // mPaintSimple.setColor(new Color(selectedOldColor));
            mPaintSimple.setAlpha(updateAlpha);
            canvas.drawCircle(clickX, clickY, Constant.FIVEZORE, mPaintSimple);
        }
    }

    /**
     * 返回参数
     *
     * @return 返回参数
     */
    public float getNumber() {
        float sizeSet = Constant.SECONDF;
        if (showType == 1) {
            if (widthPm == Constant.ONEZOREEATER) {
                sizeSet = Constant.SECONDFIVERF1080;
            } else {
                sizeSet = Constant.SECONDFIVERF;
            }
        } else if (showType == Constant.SECOND) {
            if (widthPm == Constant.ONEZOREEATER) {
                sizeSet = Constant.SECONDNORMAL1080;
            } else {
                sizeSet = Constant.SECONDNORMAL;
            }
        }
        return sizeSet;
    }

    private ColorCircle findNearestByColor(int color) {
        float[] hsvInfo = new float[Constant.THIRD];

        // color id 转RGB
        int red = (color & Constant.DEFAULT) >> Constant.ONESIX;
        int green = (color & Constant.DEFAULT2) >> Constant.EAT;
        int blue = color & Constant.DEFAULT3;

        double[] hsvColor = ColorConverUtils.rgbToHsv(red, green, blue);
        hsvInfo[0] = (float) hsvColor[0];
        hsvInfo[1] = (float) hsvColor[1];
        hsvInfo[Constant.SECOND] = (float) hsvColor[Constant.SECOND];

        ColorCircle near = null;
        double minDiff = Double.MAX_VALUE;
        double xx = hsvInfo[1] * Math.cos(hsvInfo[0] * Math.PI / Constant.ONEEAT);
        double yy = hsvInfo[1] * Math.sin(hsvInfo[0] * Math.PI / Constant.ONEEAT);

        for (ColorCircle colorCircle : renderer.getColorCircleList()) {
            float[] hsv1 = colorCircle.getHsv();
            double x1 = hsv1[1] * Math.cos(hsv1[0] * Math.PI / Constant.ONEEAT);
            double y1 = hsv1[1] * Math.sin(hsv1[0] * Math.PI / Constant.ONEEAT);
            double dx = xx - x1;
            double dy = yy - y1;
            double dist = dx * dx + dy * dy;
            if (dist < minDiff) {
                minDiff = dist;
                near = colorCircle;
            }
        }
        return near;
    }

    /**
     * 获取选择的颜色
     *
     * @return 获取选择的颜色
     */
    public int getSelectedColor() {
        int color = 0;
        if (currentColorCircle != null) {
            color = ColorConverUtils.colorAtLightness(currentColorCircle.getColor(), updateLightness);
        }
        return ColorConverUtils.adjustAlpha(updateAlpha, color);
    }

    /**
     * 添加颜色监听
     *
     * @param listener listener
     */
    public void addOnColorSelectedListener(OnColorSelectedListener listener) {
        this.listeners.add(listener);
    }

    /**
     * 触摸事件
     *
     * @param component component
     * @param touchEvent touchEvent
     * @return 触摸事件
     */
    @Override
    public boolean onTouchEvent(Component component, TouchEvent touchEvent) {
        if (touchEvent.getAction() == TouchEvent.PRIMARY_POINT_DOWN) {
            float xx = touchEvent.getPointerPosition(touchEvent.getIndex()).getX();
            float yy = touchEvent.getPointerPosition(touchEvent.getIndex()).getY();
            currentColorCircle = findNearestByPosition(xx, yy); // 弹出当前颜色值
            // 点击位置和事件监听
            int selectedColor = getSelectedColor();
            for (OnColorSelectedListener listener : listeners) {
                listener.onColorSelected(selectedColor);
            }
            selectedOldColor = selectedColor;
            isClickColor = true;
            getClickCircle(xx, yy, selectedColor);
        }
        return false;
    }

    /**
     * 点击圆圈
     *
     * @param xx xx
     * @param yy yy
     * @param selectedColor selectedColor
     */
    public void getClickCircle(float xx, float yy, int selectedColor) {
        ColorCircle colorCircle = findNearestByColor(selectedColor);
        if (colorCircle == null) {
            clickX = xx;
            clickY = yy;
        } else {
            clickX = colorCircle.getX();
            clickY = colorCircle.getY();
        }

        if (updateLightness != 0.0) {
            mPaintSimple.setColor(new Color(selectedColor));
            mPaintSimple.setStyle(Paint.Style.FILL_STYLE);
            postLayout();
            invalidate();
        }
    }

    private ColorCircle findNearestByPosition(float x, float y) {
        ColorCircle near = null;
        double minDist = Double.MAX_VALUE;
        for (ColorCircle colorCircle : renderer.getColorCircleList()) {
            double dist = colorCircle.sqDist(x, y);
            if (minDist > dist) {
                minDist = dist;
                near = colorCircle;
            }
        }
        return near;
    }

    /**
     * updateAlphaValue
     *
     * @param alphaType alphaType
     */
    public void updateAlphaValue(float alphaType) {
        updateAlpha = alphaType;
        COLORCIRCLELIST.clear();
        int selectedColor = getSelectedColor();
        for (OnColorSelectedListener listener : listeners) {
            listener.onColorSelected(selectedColor);
        }
        selectedOldColor = selectedColor;
        clearDraw();
    }

    /**
     * updateLightnessValue
     *
     * @param lightnessType lightnessType
     */
    public void updateLightnessValue(float lightnessType) {
        updateLightness = lightnessType;
        COLORCIRCLELIST.clear();
        int selectedColor = getSelectedColor();
        for (OnColorSelectedListener listener : listeners) {
            listener.onColorSelected(selectedColor);
        }
        selectedOldColor = selectedColor;
        clearDraw();
    }

    /**
     * clearDraw
     */
    public void clearDraw() {
        if (canvasSimple != null) {
            canvasSimple.drawColor(Color.TRANSPARENT.getValue(), Canvas.PorterDuffMode.CLEAR); // 清理画布
        }
        postLayout();
        mPaint.reset();
        mPaintSimple.reset();
        invalidate();
    }

    /**
     * WHEEL_TYPE
     *
     * @author ColorPicker
     * @since 2021-04-16
     */
    public enum WheelType {
        /**
         * Flower
         */
        Flower,
        /**
         * Circle
         */
        Circle
    }
}
